<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import * as THREE from "three";
import Stats from "three/examples/jsm/libs/stats.module";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
import { ref, onMounted } from "vue";

const canvas = ref();
// 场景
const scene = new THREE.Scene();
// 渲染器
const renderer = new THREE.WebGLRenderer({ antialias : true, });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.shadowMap.enabled = true;
// 相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight);
camera.position.set(0, 40, 50);
camera.lookAt(scene.position);
// 相机控件
const orbitControl = new OrbitControls(camera, renderer.domElement);
orbitControl.update();
// 性能统计
const stat = Stats();
// cube and gui
const cubeMaterial = new THREE.MeshNormalMaterial({
  transparent: true,
  opacity: 0.5
});
const createCube = () => {
  const cubeGeometry = new THREE.BoxGeometry(1.0, 1.0, 1.0);

  const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
  cube.castShadow = true;

  // position the cube randomly in the scene
  cube.position.x = -60 + Math.round((Math.random() * 100));
  cube.position.y = Math.round((Math.random() * 10));
  cube.position.z = -150 + Math.round((Math.random() * 175));

  return cube;
};

const controls = {
  cameraNear: camera.near,
  cameraFar: camera.far,
  rotationSpeed: 0.02,
  combined: false,
  numberOfObjects: 1000,
  redraw: () => {
    // 先进行删除
    const toRemove = [];
    scene.traverse((e) => {
      if (e instanceof THREE.Mesh) {
        toRemove.push(e);
      }
    });
    toRemove.forEach((e) => {
      scene.remove(e);
    });
    // 再新增 controls.numberOfObjects 个 cube
    if (controls.combined) {
      // 合并模式，进行性能优化
      const geometryArr = [];
      for (let i = 0; i < controls.numberOfObjects; i++) {
        const cube = createCube();
        cube.updateMatrix();
        geometryArr.push(cube.geometry.applyMatrix4(cube.matrix));
      }
      const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(geometryArr);
      scene.add(new THREE.Mesh(mergedGeometry, cubeMaterial));
    } else {
      for (let i = 0; i < controls.numberOfObjects; i++) {
        scene.add(createCube());
      }
    }
  },
  outputObjects: () => {
    console.log("scene.children: ", scene.children);
  },
};

const gui = new GUI();
// @ts-ignore
gui.add(controls, 'numberOfObjects', 0, 20000).name("cube数量").step(100);
// @ts-ignore
gui.add(controls, 'combined').name("合并Cube").onChange(controls.redraw);
// @ts-ignore
gui.add(controls, 'redraw').name("重绘");
// @ts-ignore
gui.add(controls, 'outputObjects').name("输出cube信息");

controls.redraw();

let rotation = 0;
const render = () => {
  stat.update();

  rotation += 0.005;
  camera.position.x = Math.sin(rotation) * 50;
  // camera.position.y = Math.sin(rotation) * 40;
  camera.position.z = Math.cos(rotation) * 50;
  camera.lookAt(scene.position);

  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

onMounted(() => {
  canvas.value.appendChild(stat.domElement);
  canvas.value.appendChild(renderer.domElement);
  render();
});
</script>
